package shortcircuit.basics;


import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.util.ArrayList;
import java.util.Random;

import shortcircuit.basics.powerup.*;


/**
 * This class will control all of the settings related to each of the blocks in the
 * game.<p>
 * The block will store its actual unrounded speed and position components, unrelated
 * to any grid form.  Each block will contain a set of 4 walls in each direction and booleans
 * that relate to falling and hovering status.
 * 
 * @author Kevin Lewi
 */

public abstract class Block implements Element
{
	protected Image theImage;
	protected String blockDesignFilePath;
	private Point _Position, originalPosition;
	private double _Speed, _Time;
	private float _Transparency, _Brightness;
	private boolean isFaded = true, isBleached = true;
	protected Wall[] _Walls;
	private RescaleOp rop;
	public float[] scales;
	private PowerUp powerUp;
	
	/**
	 * Sets the initial values for the creation of a block.  Default constructor used for
	 * when created blocks are falling.
	 * 
	 * @param thePosition Initial position of the block
	 * @param theFreeWalls The array of directions of the free walls
	 * @author Kevin Lewi
	 */
	public Block(Point thePosition, int[] theFreeWalls)
	{
		_Transparency = 1.0f;
		_Brightness = 1.0f;
		originalPosition = thePosition;
		_Position = originalPosition;
		_Speed = 0.0;
		_Time = 0.0;
		_Walls = new Wall[4];
		for(int index = 0; index < _Walls.length; index++)
		{
			_Walls[index] = null;
			_Walls[index] = new Wall(index, false);
		}
		
		applyFreeWalls(theFreeWalls);
	}
	
	public void generatePowerUp(int theLevel)
	{
		Random randGen = new Random();
		if(Math.abs(randGen.nextInt()) % (20 + 3 * theLevel) == 0)
			powerUp = new Bomb();
		else
			powerUp = new Null();
	}
	
	public void setPowerUp(PowerUp thePowerUp)
	{
		powerUp = thePowerUp;
	}
	
	/**
	 * Sets the initial values for the creation of a block.  Default constructor used for
	 * when created blocks are stationary.
	 * 
	 * @author Kevin Lewi
	 */
	public Block()
	{
		//generatePowerUp();
		_Transparency = 1.0f;
		_Brightness = 1.0f;
		_Speed = 0.0;
		_Time = 0.0;
		_Walls = new Wall[4];
		for(int index = 0; index < _Walls.length; index++)
		{
			_Walls[index] = null;
			_Walls[index] = new Wall(index, false);
		}
	}
	
	public float getTransparency()
	{
		return _Transparency;
	}
	
	public void setTransparency(float theTransparency)
	{
		_Transparency = theTransparency;
	}
	
	public void setBrightness(float theBrightness){
		_Brightness = theBrightness;
	}
	
	/**
	 * Sets the free walls of the block via the passed in array of walls.
	 * 
	 * @param theFreeWalls The array of directions of the free walls
	 * @author Kevin Lewi
	 */
	protected void applyFreeWalls(int[] theFreeWalls)
	{
		for(int index = 0; index < theFreeWalls.length; index++)
			getWall(theFreeWalls[index]).setFree(true);
	}
	
	/**
	 * Returns the color of the block.  This will differ for each type of block.
	 * 
	 * @return The color of the block
	 * @author Kevin Lewi
	 */
	protected abstract Color getBlockColor();
	
	/**
	 * Draws a colored block depending on the type of block and adds in wall graphics.
	 * 
	 * @author Kevin Lewi
	 */
	public void getGraphics(Graphics g)
	{
		Graphics2D g2D = (Graphics2D) g;
		if(_Transparency != 1.0f || _Brightness != 1.0f || scales == null)
		{
			scales = new float[4];
			for(int i = 0; i < scales.length; i++)
			{
				if(i == scales.length - 1)
					scales[i] = _Transparency;
				else
					scales[i] = _Brightness;
			}
		}
		if(_Transparency < 0.0f)
			scales[3] = 0.0f;
		if(_Transparency > 1.0f){
			scales[3] = 1.0f;
		}
	    
	    float[] offsets = new float[4];
		rop = new RescaleOp(scales, offsets, null);
		
		getBlockDesign(g2D, new Point());
	}
	
	public void getGraphics(Graphics g, Point thePosition)
	{
		Graphics2D g2D = (Graphics2D) g;
		if(_Transparency != 1.0f || _Brightness != 1.0f || scales == null)
		{
			scales = new float[4];
			for(int i = 0; i < scales.length; i++)
			{
				if(i == scales.length - 1)
					scales[i] = _Transparency;
				else
					scales[i] = _Brightness;
			}
		}
		if(_Transparency < 0.0f)
			scales[3] = 0.0f;
		if(_Transparency > 1.0f){
			scales[3] = 1.0f;
		}
	    
	    float[] offsets = new float[4];
		rop = new RescaleOp(scales, offsets, null);
		
		getBlockDesign(g2D, thePosition);
	}
	
	/**
	 * Returns the design of the block showing how each of the walls are linked.
	 * 
	 * @param g The graphics object
	 * @author Kevin Lewi
	 */
	private void getBlockDesign(Graphics g, Point thePosition)
	{
		BufferedImage bi = new BufferedImage(theImage.getWidth(null), theImage.getHeight(null), BufferedImage.TYPE_INT_ARGB);
		Graphics2D g2D = (Graphics2D) g;
		bi.getGraphics().drawImage(theImage, 0, 0, null);
		if(theImage != null)
			g2D.drawImage(bi, rop, thePosition.x, thePosition.y);
		if(hasPowerUp())
		{
			Image powerUpImage = getPowerUp().getImage();
	
			bi = new BufferedImage(40, 40, BufferedImage.TYPE_INT_ARGB);
			bi.getGraphics().drawImage(powerUpImage, thePosition.x, thePosition.y, null);
			g2D.drawImage(bi, rop, thePosition.x, thePosition.y);
		}
	}
	
	public void removePowerUp()
	{
		powerUp = new Null();
	}
	
	public boolean hasPowerUp()
	{
		return powerUp != null && !powerUp.getName().contains("null");
	}
	
	public PowerUp getPowerUp()
	{
		return powerUp;
	}
	
	/**
	 * Returns the wall from the specified direction of the block.
	 * 
	 * @param theDirection The direction of the wall
	 * @return The wall in the specified direction
	 * @author Kevin Lewi
	 */
	public Wall getWall(int theDirection)
	{
		return _Walls[theDirection];
	}

	/**
	 * Returns the walls of the block that are free.
	 * 
	 * @return ArrayList of walls that are free on the block
	 * @deprecated
	 * @author Kevin Lewi
	 */
	public ArrayList<Wall> getFreeWalls()
	{
		ArrayList<Wall> freeWalls = new ArrayList<Wall>();
		for(int index = 0; index < _Walls.length; index++)
			if(_Walls[index].isFree())
				freeWalls.add(_Walls[index]);
		return freeWalls;
	}
	
	public boolean fade()
	{
		if(_Transparency >= 0f)
			isFaded = true;
		if(isFaded)
			_Transparency -= 0.05f;
		if(_Transparency <0f)
		{
			_Transparency = 0.0f;
			isFaded = false;
			return true;
		}
		return !isFaded;
	}
	
	public boolean bleach(){
		if(_Brightness < 1f)
			isBleached = true;
		if(isBleached)
			_Brightness += 0.5;
		if(_Brightness > 10f){
			isBleached = false;
			return true;
		}
		return !isBleached;
	}
	
	/**
	 * Advances the block by recalculating a new speed and position.<p>
	 * Speed is measured in pixels per frame, terminal velocity of 60.
	 * 
	 * @author Kevin Lewi
	 */
	public void step()
	{
		_Time+=0.005;
		_Speed = EnvironmentPane.ACCELERATION * _Time;
		if(_Speed < 60);
			_Speed = 60;
		_Position.y = (int) (originalPosition.y + _Speed * _Time);
	}
	
	/**
	 * The graphics for the affected walls are set to be on fire.  The flaming graphics will
	 * animate accordingly per frame.
	 * 
	 * @param affectedWalls The array of wall directiosn that need to be set on fire
	 * @author Kevin Lewi
	 */
	public void setWallsOnFire(int[] affectedWalls)
	{
		for(int index = 0; index < affectedWalls.length; index++)
			getWall(affectedWalls[index]).setOnFire(true);
	}
	
	/**
	 * Clears all walls of their fired status.
	 * 
	 * @author Kevin Lewi
	 */
	public void clearFiredWalls()
	{
		for(int index = 0; index < 4; index++)
			getWall(index).setOnFire(false);
	}
	
	public void reset(){
		_Speed = 0.0;
		_Time = 0.0;
	}
	
	/**
	 * Returns the position of the block.
	 * 
	 * @return The position of the block
	 * @author Kevin Lewi
	 */
	public Point getPosition()
	{
		return _Position;
	}
	
	/**
	 * Sets the original position of the block.
	 * 
	 * @param thePosition The original position of the block
	 * @author Kevin Lewi
	 */
	public void setOrigPosition(Point thePosition)
	{
		originalPosition = thePosition;
		_Position = originalPosition;
	}

	public Image getTheImage() {
		return theImage;
	}

	public void setTheImage(Image theImage) {
		this.theImage = theImage;
	}
}
